/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of UHM (Unassembled HyperMatrix) sparse direct solver.
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/
#ifndef UHM_MESH_ELEMENT_HXX
#define UHM_MESH_ELEMENT_HXX


namespace uhm {
  typedef class Mesh_*    Mesh;
  typedef class Node_*    Node;
  typedef class Element_* Element;
  typedef class Matrix_*  Matrix;

  bool element_valid(Element e);

  // ----------------------------------------------------------------
  // ** Element class
  class Element_ : public Object_< int > {
  protected:
    // generation of elements
    int generation; 
    
    // tree information :: support only binary tree
    Element parent;
    std::vector< Element > children;
    
    // node, 0 - not separated, 1 factor, 2 schur
    std::map   < Node, int > nodes;

    // node, offset
    std::vector< std::pair<Node, int> > factor, schur;
    
    // matrix objects
    Matrix hm;

    bool   reuse, visit;  // reuse flag
    int    marker[2];     // build_tree_var_2 need marker

    void _init(int id, int gen);
    
  public:
    Element_();
    Element_(int id);
    Element_(int id, int gen);
    virtual ~Element_();

    virtual bool disp();
    virtual bool disp(FILE *stream);

    bool operator<(const Element_ &b) const;
    
    
    // reset operation does not affect to realted objects
    // use reset for global operation
    void reset_parent();
    void reset_children();
    void reset_nodes();
    void reset_factor();
    void reset_schur();
    
    void exclude_non_boundary_nodes();
    
    void update_generation();

    void set_generation(int gen);
    void set_matrix(Matrix hm);
    void set_reuse(int flag);
    void set_visit(int visit);
    void set_marker(int index, int marker);
    void set_parent(Element p);

    void set_nodes_kind(int kind);
    void set_nodes_kind(int kind, int separated);

    int  get_generation();
    int  get_height();

    Element get_parent();
    Element get_child(int loc);
    Matrix  get_matrix();
    int     get_marker(int index);

    int  get_n_children();
    int  get_n_nodes();
    int  get_n_factor_nodes();
    int  get_n_schur_nodes();

    std::pair<int,int> get_n_dof();

    void estimate_cost( int method, int datatype, int n_rhs,
                        double &flop_decompose, double &flop_solve,
                        double &buffer );

    bool is_orphan();
    bool is_leaf();
    bool is_nodes_separated();
    bool is_nodes_arranged();
    bool is_matrix_created();
    bool is_matrix_reusable();
    bool is_visited();

    void collect_leaf_children( int n_max, int &n_leaves, Element *leaves );
    void collect_leaf_children( std::vector< Element > &leaves );

    void add_child (Element c);

    void add_node  (Node n);
    void add_node  (Node n, int separated);
    void add_schur (Node n, int offs);
    void add_factor(Node n, int offs);
        
    void separate_nodes();
    void arrange_nodes();
    void merge_nodes_from_children();
    void merge_nodes(Element c);

    void restore_connectivity();
    void numbering();

    // ** Dirty... clean up followings
    bool write_graphviz_hier(FILE *fp, int is_leaf2root, double max_n_dof);
    bool sparse_pattern(std::set< std::pair<int,int> > &s, int mat);

    bool export_matrix(FILE *stream, int n_rhs, int mat);
    bool export_matrix(int n_rhs, 
		       int &m, int &n, 
		       std::vector< std::pair<int,int> > &ij, 
		       int mat);

    // ** I prefere these more
    bool export_atl_matrix     (std::vector< int > &ij);
    bool export_abr_matrix     (std::vector< int > &ij);
    bool export_a_matrix       (std::vector< int > &ij);
    bool export_diag_matrix    (int mat, std::vector< int > &ij);

    bool export_abl_matrix     (std::vector< int > &i, std::vector< int > &j);
    bool export_atr_matrix     (std::vector< int > &i, std::vector< int > &j);
    bool export_a_matrix       (int is_trans, 
                                std::vector< int > &i, std::vector< int > &j);
    bool export_off_diag_matrix(int mat, 
                                std::vector< int > &i, std::vector< int > &j);
          

    // ** Why do I have this ?
    void write_lu_nopiv(FILE *fp, int bmn, int &start, int &end);
    
    friend class Mesh_;
    friend class Helper_;
    friend bool build_graph(std::vector< Element > *orphan,
			     Element parent,
			     //
			     std::vector< int > *xadj,
			     std::vector< int > *adjncy,
			     std::vector< int > *adjwgt);

    friend bool build_tree_var_1(Mesh m);

    friend bool element_valid(Element e);
  };

  // ----------------------------------------------------------------
  // ** Definition
  inline void Element_::_init(int id, int gen) {
    //    std::pair<int,int> zero(0,0);
    this->id         = id;
    this->cookie     = UHM_ELEMENT_COOKIE;
    this->generation = gen;
    this->parent     = nil_element;
    this->hm         = nil_matrix;
    this->reuse      = false;
    this->visit      = false;

    for (int i=0;i<2;++i) {
      this->marker[i] = 0;
    }
  }
  inline bool Element_::operator<(const Element_ &b) const { 
    return (this->id < b.id); 
  }
  inline bool element_valid(Element e) { 
    return (e && e->cookie == UHM_ELEMENT_COOKIE); 
  }

}

#endif
